package com.atguigu.gmall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.product.mapper.SkuAttrValueMapper;
import com.atguigu.gmall.product.mapper.SkuSaleAttrValueMapper;
import com.atguigu.gmall.product.model.*;
import com.atguigu.gmall.product.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author: atguigu
 * @create: 2023-02-24 11:40
 */
@Slf4j
@Service
public class SkuManageServiceImpl implements SkuManageService {

    @Autowired
    private SkuInfoService skuInfoService;

    @Autowired
    private SkuImageService skuImageService;

    @Autowired
    private SkuAttrValueService skuAttrValueService;

    @Autowired
    private SkuSaleAttrValueService skuSaleAttrValueService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RabbitService rabbitService;


    /**
     * 保存商品SKU信息
     * 1.将前端提交商品SKU基本信息存入到sku_info表
     * 2.将前端提交当前SKU相关图片集合 存入到 sku_image表
     * 3.将前端提交当前SKU的平台属性信息 存入到 sku_attr_value表
     * 4.将前端提交当前SKU的销售属性信息 存入到 sku_sale_attr_value 表
     *
     * @param skuInfo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSkuInfo(SkuInfo skuInfo) {
        //开启事务

        //1.将前端提交商品SKU基本信息存入到sku_info表
        skuInfoService.save(skuInfo);
        //2.将前端提交当前SKU相关图片集合 存入到 sku_image表
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if (!CollectionUtils.isEmpty(skuImageList)) {
            skuImageList.stream().forEach(skuImage -> {
                //2.1 将商品SKU图片关联到SKU
                skuImage.setSkuId(skuInfo.getId());
            });
            //2.2 批量保存商品SKU图片
            skuImageService.saveBatch(skuImageList);
        }
        //3.将前端提交当前SKU的平台属性信息 存入到 sku_attr_value表
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        if (!CollectionUtils.isEmpty(skuAttrValueList)) {
            //2.1 将平台属性信息关联到SKU
            skuAttrValueList.stream().forEach(skuAttrValue -> {
                skuAttrValue.setSkuId(skuInfo.getId());
            });
            //2.2 批量保存平台属性
            skuAttrValueService.saveBatch(skuAttrValueList);
        }
        //4.将前端提交当前SKU的销售属性信息 存入到 sku_sale_attr_value 表
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        if (!CollectionUtils.isEmpty(skuSaleAttrValueList)) {
            skuSaleAttrValueList.stream().forEach(skuSaleAttrValue -> {
                //销售属性关联SPU
                skuSaleAttrValue.setSpuId(skuInfo.getSpuId());
                //销售属性关联SKU
                skuSaleAttrValue.setSkuId(skuInfo.getId());
            });
            skuSaleAttrValueService.saveBatch(skuSaleAttrValueList);
        }

        //5.将保持的商品SKUID存入布隆过滤器
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);
        bloomFilter.add(skuInfo.getId());
    }


    /**
     * 根据分类ID分页查询商品SKU列表
     *
     * @param iPage
     * @param category3Id
     * @return
     */
    @Override
    public IPage<SkuInfo> getSkuListByPage(IPage<SkuInfo> iPage, Long category3Id) {
        LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
        if (category3Id != null) {
            queryWrapper.eq(SkuInfo::getCategory3Id, category3Id);
        }
        queryWrapper.orderByDesc(SkuInfo::getUpdateTime);
        return skuInfoService.page(iPage, queryWrapper);
    }


    /**
     * 商品SKU上架
     *
     * @param skuId
     * @return
     */
    @Override
    public void onSale(Long skuId) {
        //1.修改数据库中上架状态
        SkuInfo skuInfo = new SkuInfo();
        skuInfo.setId(skuId);
        skuInfo.setIsSale(1);
        skuInfoService.updateById(skuInfo);
        //2.将来还需要同步将索引库ES的商品进行上架;需要构建商品缓存到Redis
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS, MqConst.ROUTING_GOODS_UPPER, skuId);
    }

    /**
     * 商品SKU下架
     *
     * @param skuId
     * @return
     */
    @Override
    public void cancelSale(Long skuId) {
        //SkuInfo skuInfo = skuInfoService.getById(skuId);
        //if (skuInfo != null && skuInfo.getIsSale() != 0) {
        //    skuInfo.setIsSale(0);
        //    skuInfoService.updateById(skuInfo);
        //}
        LambdaUpdateWrapper<SkuInfo> updateWrapper = new LambdaUpdateWrapper<>();
        //1.设置更新条件
        updateWrapper.eq(SkuInfo::getId, skuId);
        //1.设置更新字段值
        updateWrapper.set(SkuInfo::getIsSale, 0);
        skuInfoService.update(updateWrapper);

        //2. 将来还需要同步将索引库ES的商品进行下架;需要删除商品缓存Redis
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS, MqConst.ROUTING_GOODS_LOWER, skuId);
    }


    /**
     * 查询商品信息优化 采用Redisson实现分布式锁
     * 1.优先从缓存中获取数据
     * 2.获取分布式锁
     * 3.执行查询数据库逻辑
     *
     * @param skuId
     * @return
     */
    //@Override
    //public SkuInfo getSkuInfoAndImages(Long skuId) {
    //    try {
    //        //1.优先从分布式缓存Redis 中获取数据
    //        //1.1 构建商品缓存业务Key 形式: sku:商品ID:info
    //        String dataKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
    //        //1.2 从Redis分布式缓存中获取数据  有值:直接返回 没有:获取分布式锁后执行业务
    //        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(dataKey);
    //        if (skuInfo == null) {
    //            //2.尝试获取分布锁-采用Redisson实现分布式锁
    //            ///2.1 构建商品锁名称 形式:sku:商品id:lock
    //            String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
    //            RLock lock = redissonClient.getLock(lockKey);
    //            try {
    //                //2.2 创建锁对象,尝试获取锁
    //                boolean flag = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX1, RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
    //                if (flag) {
    //                    //2.3 执行查询数据库业务
    //                    //2.3.1 查询商品信息
    //                    skuInfo = this.getSkuInfoAndImagesFromDB(skuId);
    //                    if (skuInfo == null) {
    //                        //将null对象暂存到redis
    //                        redisTemplate.opsForValue().set(dataKey, skuInfo, RedisConst.SKUKEY_TIMEOUT, TimeUnit.MILLISECONDS);
    //                        return skuInfo;
    //                    } else {
    //                        //2.3.2 将商品信息放入缓存
    //                        redisTemplate.opsForValue().set(dataKey, skuInfo, RedisConst.SKUKEY_TIMEOUT, TimeUnit.SECONDS);
    //                        //2.3.3 返回结果
    //                        return skuInfo;
    //                    }
    //                } else {
    //                    //获取锁失败,自旋
    //                    return this.getSkuInfoAndImages(skuId);
    //                }
    //
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            } finally {
    //                //3.业务执行完毕,将锁释放掉
    //                lock.unlock();
    //            }
    //
    //        } else {
    //            //命中缓存数据,直接返回
    //            return skuInfo;
    //        }
    //    } catch (Exception e) {
    //        e.printStackTrace();
    //    }
    //    //兜底操作:查询数据库
    //    return this.getSkuInfoAndImagesFromDB(skuId);
    //}


    /**
     * 查询商品信息优化 采用SDR-SprignDataRedis实现分布式锁,存在问题:
     * 问题1:业务耗时超过锁有效时间,锁无法进行续期(看门狗线程)
     * 问题2:集群环境下主节点写入锁成功后,还没有同步数据到从节点.主节点宕机(RedLock红锁,只有写入数据到一半以上服务器返回成功)
     * 1.优先从缓存中获取数据
     * 2.获取分布式锁
     * 3.执行查询数据库逻辑
     *
     * @param skuId
     * @return
     */
    //@Override
    //public SkuInfo getSkuInfoAndImagesRedis(Long skuId) {
    //    try {
    //        String lockKey = "";
    //        String lockVal = "";
    //        //1.优先从Redis分布式缓存中获取业务数据 存在:直接返回  不存在:获取分布式锁
    //        //1.1 构建商品信息业务数据缓存Key 形式--  sku:24:info
    //        String dataKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
    //        //1.2 通过业务Key从分布式缓存中获取业务数据
    //        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(dataKey);
    //        if (skuInfo == null) {
    //            try {
    //                //2.尝试获取分布式锁  成功:执行查询数据库逻辑,放入缓存返回 失败:自旋
    //                lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
    //                //2.1 构建当前商品锁值 UUID
    //                lockVal = UUID.randomUUID().toString().replaceAll("-", "");
    //                //2.2 调用set ex nx 获取锁
    //                Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, RedisConst.SKULOCK_EXPIRE_PX1, TimeUnit.SECONDS);
    //                if (flag) {
    //                    //2.3 执行业务逻辑-从数据库中查询商品信息
    //                    skuInfo = this.getSkuInfoAndImagesFromDB(skuId);
    //                    //2.3.1 查询数据库获取业务数据  有值:放入缓存,响应业务数据  没有:null放入缓存(暂存)
    //                    if (skuInfo == null) {
    //                        redisTemplate.opsForValue().set(dataKey, skuId, RedisConst.SKUKEY_TIMEOUT, TimeUnit.MILLISECONDS);
    //                        return skuInfo;
    //                    }
    //                    //3.3.2 数据库中有数据,将业务数据放入缓存,返回业务数据
    //                    redisTemplate.opsForValue().set(dataKey, skuInfo, RedisConst.SKUKEY_TIMEOUT, TimeUnit.SECONDS);
    //                    return skuInfo;
    //                } else {
    //                    try {
    //                        //本次获取锁失败-自旋
    //                        Thread.sleep(100);
    //                        this.getSkuInfoAndImages(skuId);
    //                    } catch (InterruptedException e) {
    //                        e.printStackTrace();
    //                    }
    //                }
    //            } catch (Exception e) {
    //                e.printStackTrace();
    //            } finally {
    //                //3.将锁释放 采用lua脚本释放锁
    //                String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
    //                        "then\n" +
    //                        "    return redis.call(\"del\",KEYS[1])\n" +
    //                        "else\n" +
    //                        "    return 0\n" +
    //                        "end";
    //                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
    //                redisScript.setScriptText(scriptText);
    //                redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
    //            }
    //        } else {
    //            //缓存中业务数据直接返回即可
    //            return skuInfo;
    //        }
    //    } catch (Exception e) {
    //        e.printStackTrace();
    //    }
    //    //兜底处理-查询数据库
    //    return this.getSkuInfoAndImagesFromDB(skuId);
    //}


    /**
     * 根据SkuID查询sku信息以及图片
     *
     * @param skuId
     * @return
     */
    @GmallCache(prefix = RedisConst.SKUKEY_PREFIX, suffix = RedisConst.SKUKEY_SUFFIX)
    public SkuInfo getSkuInfoAndImages(Long skuId) {
        //1.根据主键ID查询商品Sku信息
        SkuInfo skuInfo = skuInfoService.getById(skuId);
        //2.根据SKuID查询商品SKU的图片列表
        if (skuInfo != null) {
            LambdaQueryWrapper<SkuImage> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SkuImage::getSkuId, skuId);
            List<SkuImage> skuImageList = skuImageService.list(queryWrapper);
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }

    /**
     * 根据SKUID查询商品最新价格
     *
     * @param skuId
     * @return
     */
    @Override
    public BigDecimal getSkuPrice(Long skuId) {
        //1.创建锁对象 获取锁
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + "price";
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean flag = lock.tryLock(RedisConst.SKULOCK_EXPIRE_PX1, RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
            if (flag) {
                //2.获取锁成功后执行业务
                LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(SkuInfo::getId, skuId);
                //指定查询字段
                queryWrapper.select(SkuInfo::getPrice);
                SkuInfo skuInfo = skuInfoService.getOne(queryWrapper);
                if (skuInfo != null) {
                    return skuInfo.getPrice();
                }
            }
            return null;
        } catch (Exception e) {
            log.error("获取价格异常:{}", e);
        } finally {
            //3.将锁释放
            lock.unlock();
        }
        return new BigDecimal("0");
    }

    /**
     * 根据SkuID查询当前商品包含平台属性以及属性值
     *
     * @param skuId
     * @return
     */
    @Override
    @GmallCache(prefix = "attrList:")
    public List<BaseAttrInfo> getAttrList(Long skuId) {
        SkuAttrValueMapper skuAttrValueMapper = (SkuAttrValueMapper) skuAttrValueService.getBaseMapper();
        List<BaseAttrInfo> attrList = skuAttrValueMapper.getAttrList(skuId);
        if (!CollectionUtils.isEmpty(attrList)) {
            for (BaseAttrInfo baseAttrInfo : attrList) {
                baseAttrInfo.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());
            }
        }
        return attrList;
    }

    /**
     * 获取每一组销售属性对应SkuID组合 {"3736|3738":"24","3736|3739":"25",}
     *
     * @param spuId
     * @return
     */
    @Override
    @GmallCache(prefix = "skuValueIdsMap:")
    public String getSkuValueIdsMap(Long spuId) {
        //声明封装所有销售属性跟SKUID对应Map
        HashMap<String, String> mapResult = new HashMap<>();
        //查询自定义SQL
        SkuSaleAttrValueMapper skuSaleAttrValueMapper = (SkuSaleAttrValueMapper) skuSaleAttrValueService.getBaseMapper();
        List<Map> list = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        if (!CollectionUtils.isEmpty(list)) {
            //遍历List获取销售属性以及SKUID
            list.stream().forEach(map -> {
                Long skuId = (Long) map.get("sku_id");
                String valueIds = (String) map.get("value_ids");
                mapResult.put(valueIds, skuId.toString());
            });
        }
        return JSON.toJSONString(mapResult);
    }


}
